﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Compilex.Automata;
using System.IO;
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests.Lexic.TestCases
{
    public class LexerTestCase
    {
        public static WordToken WordTokenFromAttrToken<T>(AttributeToken<T> attr)
        {
            return new WordToken(attr.Name, attr.Attribute.ToString());
        }
        public string Source { get; set; }
        public Token[] ExpectedTokens { get; set; }
        public LanguageConfiguration Language { get; set; }

        public LexerTestCase(string textCase)
        {
            string[] fileLines = textCase.Split(new string[] { Environment.NewLine },
                StringSplitOptions.None);
            Language = new LanguageConfiguration(null, null);
            ParseTextCase(fileLines);
        }

        public void RunTest()
        {
            Lexer lexer = new Lexer(this.Language, this.Source);
            Token[] returnedTokens = lexer.Analyze();
            Assert.AreEqual(ExpectedTokens.Length, returnedTokens.Length);
            for (int i = 0; i < returnedTokens.Length; i++)
            {
                Assert.AreEqual(ExpectedTokens[i], returnedTokens[i]);
            }
        }

        private void ParseTextCase(string[] fileLines)
        {
            List<string> keywords = new List<string>();
            List<string> operators = new List<string>();
            List<string> expectedTokens = new List<string>();

            int i = 0;
            while (i < fileLines.Length && fileLines[i++] != "")
            {
                keywords.Add(fileLines[i-1].Trim());
            }
            while (i < fileLines.Length && fileLines[i++] != "")
            {
                operators.Add(fileLines[i - 1].Trim());
            }
            while (i < fileLines.Length && fileLines[i++] != "")
            {
                string line = fileLines[i - 1];
                string[] tokensOnThisLine = line.Split(new char[]{'<'},
                    StringSplitOptions.RemoveEmptyEntries);
                expectedTokens.AddRange(tokensOnThisLine);
            }
            this.Source = "";
            while (i < fileLines.Length)
            {
                this.Source += fileLines[i++] + "\r\n";
            }

            // retira o último \r\n
            this.Source = this.Source.Substring(0, this.Source.Length - 2);

            ParseKeywords(keywords);
            ParseOperators(operators);
            ParseExpectedTokens(expectedTokens);
        }

        private void ParseExpectedTokens(List<string> expectedTokens)
        {
            List<Token> tokens = new List<Token>();
            foreach (var strexpectedToken in expectedTokens)
            {
                string expectedToken = strexpectedToken.Trim();
                Debug.Assert(expectedToken.EndsWith(">"));
                string[] elements = expectedToken.Substring(0, expectedToken.Length - 1)
                    .Trim().Split(',');
                Debug.Assert(elements.Length > 0);
                if (elements.Length == 1)
                {
                    tokens.Add(new Token(elements[0].Trim()));
                }
                else
                {
                    Token token = null;
                    switch (elements[0].Trim())
                    {
                        case IdentifierToken.TokenName:
                            token = new  IdentifierToken(elements[1].Trim());
                            break;
                        case IntToken.TokenName:
                            token = new IntToken(int.Parse(elements[1].Trim()));
                            break;
                        case FloatToken.TokenName:
                            token = new FloatToken(float.Parse(elements[1].Trim()));
                            break;
                        case LiteralToken.TokenName:
                            token = new LiteralToken(elements[1].Trim());
                            break;
                        default:
                            token = new Token(elements[0].Trim());
                            break;
                    }
                    tokens.Add(token);
                }
            }

            this.ExpectedTokens = tokens.ToArray();
        }

        private void ParseOperators(List<string> strOperators)
        {
            List<Operator> operators = new List<Operator>();
            foreach (string _operator in strOperators)
            {
                // OP_ADD +
                string[] elements = _operator.Split(' ');
                operators.Add(new Operator(elements[0], elements[1]));
            }
            this.Language.operators = operators.ToArray();
        }

        private void ParseKeywords(List<string> keywords)
        {
            Language.reservedWords = keywords.ToArray();
        }

    }
}
